import { Injectable } from '@nestjs/common';
import { IpcSuccessException } from '@main/common/exceptions/ipc.exception';
import { networkInterfaces } from 'os';
import { getConfigFn, setConfigFn } from '@main/config';
import * as net from 'net';
import { HttpService } from '@nestjs/axios';
import { app } from 'electron';
import isZip from 'is-zip';
import unzipper from 'unzipper';
import * as fs from 'fs';
import { Window } from '@doubleshot/nest-electron';
import type { BrowserWindow } from 'electron';
import SysOperationLog from '@main/entities/service/admin/sys/sys-operation-log.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import path from 'path';
import { join } from 'path';
import * as compressing from 'compressing';
import { getMainWinFn } from '@main/ipc/ipc.module';
import {
  connectInitFn,
  connectFn,
  deleteChildProcessFn,
  getPlcDataFn,
} from '@main/plc/childProcess';

// import * as fse from 'fs-extra';

@Injectable()
export class IpcService {
  constructor(
    @InjectRepository(SysOperationLog) private operationLogRepository: Repository<SysOperationLog>,
    private readonly httpService: HttpService,
    @Window() private readonly mainWin: BrowserWindow
  ) {}

  public getTime(): number {
    return new Date().getTime();
  }

  async pingIp(ip: string): Promise<boolean> {
    return new Promise<boolean>((resolve, reject) => {
      const socket = new net.Socket();

      let isReachable = false;

      socket.setTimeout(3000); // 设置超时时间为 3 秒

      socket.on('connect', () => {
        isReachable = true;
        socket.end(); // 关闭 socket 连接
      });

      socket.on('timeout', () => {
        socket.destroy(); // 销毁 socket 连接
        reject(new Error('Timeout')); // 超时时调用 reject
      });

      socket.on('error', (error) => {
        reject(error); // 出现错误时调用 reject
      });

      socket.on('close', () => {
        resolve(isReachable); // 连接关闭时调用 resolve
      });

      socket.connect(3000, ip); // 尝试连接到 IP 地址的 80 端口（可以根据需要更改端口号）
    });
  }

  isReload = false;

  host = 'localhost';

  syncSqliteTime = null;

  private async extractAndReplaceFiles(zipData: Buffer, targetFolderPath: string): Promise<void> {
    try {
      // 写入 ZIP 数据到文件
      await fs.promises.writeFile(`${app.getPath('userData')}/Zip/hostSqlite.zip`, zipData);

      const zip = await unzipper.Open.buffer(zipData);

      if (!fs.existsSync(targetFolderPath)) {
        fs.mkdirSync(targetFolderPath, { recursive: true });
      }

      await Promise.all(
        zip.files.map(async (file) => {
          const entryData = await file.buffer();
          const filePath = path.join(targetFolderPath, file.path);
          await fs.promises.writeFile(filePath, entryData);
        })
      );

      console.log('解压并替换文件完成');
    } catch (error) {
      console.error('解压出错:', error);
    }
  }

  clearTimeFn(type) {
    if (this[type]) {
      clearTimeout(this[type]);

      this[type] = null;
    }
  }

  syncSqliteFn() {
    this.clearTimeFn('syncSqliteTime');

    this.syncSqliteTime = setTimeout(
      async () => {
        try {
          const res = await this.httpService.axiosRef.get(
            `http://${this.host}:3000/http/produce/getSqlite`,
            // `http://127.0.0.1:3000/http/produce/getSqlite`,
            { responseType: 'arraybuffer' }
          );

          // console.log(res.data)

          const data = Buffer.from(res.data, 'binary');

          if (isZip(data)) {
            await this.extractAndReplaceFiles(data, `${app.getPath('userData')}/HostSqlite`);

            console.log('同步本地数据库');
          } else {
            console.log('数据库类型有误');
          }

          this.syncSqliteFn();
        } catch (error) {
          console.log('error');
          this.pingHostFn();
        }
      },
      // 30000
      30 * 1000
    );
  }

  async unzipAndCompare(zipData, extractPath) {
    const zipFilePath = `${app.getPath('userData')}/Zip/hostUploadFile.zip`;

    await fs.promises.writeFile(`${app.getPath('userData')}/Zip/hostUploadFile.zip`, zipData);

    await fs
      .createReadStream(zipFilePath)
      .pipe(unzipper.Extract({ path: extractPath }))
      .promise();

    const files = await unzipper.Open.file(zipFilePath);

    for (const file of files.files) {
      const extractedFilePath = extractPath + '/' + file.path;
      if (fs.existsSync(extractedFilePath)) {
        const localStats = fs.statSync(extractedFilePath);
        if (localStats.mtime < file.lastModified) {
          // ZIP 文件中的文件修改时间比本地文件更新，覆盖本地文件
          console.log(`Updating file: ${extractedFilePath}`);
          await file.extract({ path: extractPath, overwrite: true });
        } else {
          console.log(`Skipping file: ${extractedFilePath}`);
        }
      } else {
        // 本地不存在该文件，直接新增
        console.log(`Adding new file: ${extractedFilePath}`);
        await file.extract({ path: extractPath, overwrite: false });
      }
    }

    console.log('Extraction and comparison complete.');
  }

  syncUploadFileTime = null;

  syncUploadFileFn() {
    this.clearTimeFn('syncUploadFileTime');

    this.syncUploadFileTime = setTimeout(
      async () => {
        try {
          const res = await this.httpService.axiosRef.get(
            `http://${this.host}:3000/http/produce/getUploadFile`,
            // `http://127.0.0.1:3000/http/produce/getSqlite`,
            { responseType: 'arraybuffer' }
          );

          // console.log(res.data)

          const data = Buffer.from(res.data, 'binary');

          if (isZip(data)) {
            await this.unzipAndCompare(data, `${app.getPath('userData')}`);

            console.log('同步本地上传文件');
          } else {
            console.log('上传文件类型有误');
          }

          this.syncUploadFileFn();
        } catch (error) {
          console.log('error');
          this.pingHostFn();
        }
      },
      // 30000
      30 * 1000
    );
  }

  async syncHostSqliteFn() {
    const list: any = await this.operationLogRepository.find({
      where: {
        syncHostSqlite: 1,
      },
      order: {
        updateTime: 'ASC',
      },
    });

    console.log(list);

    try {
      if (list && list.length) {
        await this.httpService.axiosRef.post(
          `http://${this.host}:3000/http/produce/syncSqlite`,
          // `http://127.0.0.1:3000/http/produce/getSqlite`,
          list
        );
      }

      const folderPath = join(`${app.getPath('userData')}`, '/UploadFile');

      const tarStream = new compressing.zip.Stream();

      tarStream.addEntry(folderPath);

      const nodeFormData = require('form-data');

      const formData: any = new nodeFormData();

      formData.append('file', tarStream);

      await this.httpService.axiosRef.post(
        `http://${this.host}:3000/http/produce/syncUploadFile`,
        formData,
        {
          headers: formData.getHeaders(),
        }
      );
    } catch (e) {
      console.log('同步数据到主机方法报错');

      console.log(e);
    }
  }

  pingHostTime = null;

  pingHostFn() {
    this.clearTimeFn('pingHostTime');

    this.pingHostTime = setTimeout(async () => {
      const { webContents } = this.mainWin;

      // console.log(this.syncHostSqlite);

      try {
        await this.pingIp(this.host);

        if (this.syncHostSqlite) {
          console.log('在线');
          webContents.send('host-online', this.host);

          this.isReload = true;
        }
      } catch (_e) {
        if (!this.syncHostSqlite) {
          console.log('离线');
          webContents.send('host-offline', this.host);
        }

        console.log('继续ping');

        this.pingHostFn();
      }
    }, 5000);
  }

  syncHostSqlite = false;

  public async getMac() {
    const uData = getConfigFn() || ({} as any);

    let { host = 'localhost', syncHostSqlite } = uData;

    this.host = 'localhost';

    this.syncHostSqlite = false;

    this.clearTimeFn('syncSqliteTime');

    this.clearTimeFn('syncUploadFileTime');

    this.clearTimeFn('pingHostTime');

    if (this.isReload) {
      this.reload();
    }

    if (host !== 'localhost') {
      try {
        this.host = host;

        await this.pingIp(host);

        console.log(`${host} ping通`);

        if (syncHostSqlite) {
          console.log('同步数据到主机');

          await this.syncHostSqliteFn();
        }

        this.syncHostSqlite = false;

        setConfigFn({
          ...uData,
          syncHostSqlite: false,
        });

        this.syncSqliteFn();

        this.syncUploadFileFn();
      } catch (e) {
        host = 'localhost';

        setConfigFn({
          ...uData,
          syncHostSqlite: true,
        });

        this.syncHostSqlite = true;

        console.log(e);
      }

      this.pingHostFn();
    }

    const interfaces = networkInterfaces();

    let macAddress = '';

    let ipv4Address = '';

    if (interfaces.WLAN && interfaces.WLAN[0]) {
      macAddress = interfaces.WLAN[0].mac;
    } else if (interfaces['以太网'] && interfaces['以太网'][0]) {
      macAddress = interfaces['以太网'][0].mac;
    } else if (interfaces.server && interfaces.server[0]) {
      macAddress = interfaces.server[0].mac;
    }

    if (interfaces) {
      for (const interfaceName of Object.keys(interfaces)) {
        const interfaceInfos = interfaces[interfaceName];

        if (interfaceInfos) {
          for (const interfaceInfo of interfaceInfos) {
            if (
              (!macAddress || macAddress === '00:00:00:00:00:00') &&
              interfaceInfo.mac &&
              interfaceInfo.mac !== '00:00:00:00:00:00'
            ) {
              macAddress = interfaceInfo.mac;
            }

            if (
              (!ipv4Address || ipv4Address === '127.0.0.1') &&
              interfaceInfo.family === 'IPv4' &&
              !interfaceInfo.internal
            ) {
              ipv4Address = interfaceInfo.address;
            }

            if (
              macAddress &&
              macAddress !== '00:00:00:00:00:00' &&
              ipv4Address &&
              ipv4Address !== '127.0.0.1'
            ) {
              break;
            }
          }
        }

        if (macAddress.length > 0) break;
      }
    }

    return IpcSuccessException({
      mac: macAddress,
      ip: ipv4Address,
      host,
      syncHostSqlite: this.syncHostSqlite,
    });
  }

  async setConfig(data) {
    await setConfigFn({ ...data, syncHostSqlite: false });

    return IpcSuccessException();
  }

  async reload() {
    app.relaunch();

    app.exit();
  }

  connectMachineCode = null;

  plcConnect(data) {
    const { list, machineCode } = data;

    if (this.connectMachineCode === machineCode) {
      return;
    }

    const { mainWindow, ipcMain } = getMainWinFn();

    setTimeout(() => {
      connectInitFn(list, mainWindow).then((res) => {
        const { clickFn } = res;

        ipcMain.removeAllListeners('plcClick');

        ipcMain.on('plcClick', (_event, data) => {
          clickFn(data);
        });

        ipcMain.removeAllListeners('plcReConnect');

        ipcMain.on('plcReConnect', (_event, _data) => {
          connectFn();
        });

        ipcMain.removeAllListeners('getPlcData');

        ipcMain.on('getPlcData', (_event, _data) => {
          getPlcDataFn();
        });
      });
    },0);

    this.connectMachineCode = machineCode;
  }

  deleteChildProcess() {
    this.connectMachineCode = null;

    deleteChildProcessFn();
  }
}
